﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ClassLibrary1
{
    /// <summary>
    /// ref参数
    /// 首先来看一句比较绕口的话
    /// 变量作为参数传递给方法，同时希望在方法执行完成后，对参数所做的修改能够反映在原来的变量上
    /// ref参数就是为此而生
    /// </summary>
    public class RefParams
    {

        //下面做个实验
        // 1、第一步      一个方法，功能是用来交换传入参数的数值
        public void TestRef(int x, int y)
        {
            int temp;
            temp = y;
            y = x;
            x = temp;
        }
        // 2、第二部   调用TestRef方法
        public string main()
        {
            int x = 10;
            int y = 0;
            //交换前
            TestRef(x, y);
            //交换后
            return "x= " + x.ToString() + " , y= " + y.ToString();
            //返回世纪结果   “x=10,y=0”
            //可以看到本方法中两个变量x，y的值并没有得到交换
        }

        //我们明明调用了可以交换传入数值位置的函数 TestRef，为什么却没有交换 x 和 y 的位置呢？
        //原因分析:
        //          首先交换函数TestRef本身并没有问题，问题int是值类型，它所创建的实例不在托管堆中分配空间，而是直接存储在变量中。
        //          虽然我们调用了TestRef函数，但也只不过是赋值了一遍x,y的值而已，函数中操作的是赋值后的值，而不是x,y这两个值类型变量本身。




        //什么是ref？
        //   ref (全拼：reference):强制要求参数按照引用传值
        //   使用ref关键字有两个注意事项，如下：
        //       1、传入参数前必须给参数变量赋值
        //       2、调用方法时必须加ref关键字

        //所以，如下的代码将会让上面的实验成立
        public void TestRef1(ref int x, ref int y)
        {
            int temp;
            temp = y;
            y = x;
            x = temp;
        }

        public string main1()
        {
            int x = 10;
            int y = 0;
            TestRef1(ref x,ref y);
            return "x= " + x.ToString() + " , y= " + y.ToString();
            //方法将返回 “x=0 , y=10”
        }
    }



    //4.2  out参数
    //      如果方法返回一个值，该方法通常声明返回类型，并返回结果。如果方法返回多个值，可能类型还不相同，该怎么办？
    //      这有不同的方法:
    //          1、声明类或者结构作为返回类型， 把应该返回的所有信息都定义为该类或结构的成员。
    //          2、使用元组类型
    //          3、使用out输出参数
    //      在C#中，out参数用于在方法被调用时将一个或多个值从方法内部传递会调用方。与普通参数（按值传递）不同，out参数是按引用传递的，
    //          方法内部可以修改该参数的值，并且这些调用将反映到调用方

    //使用out参数时，必须注意一下几点：
    //              1、out参数在方法内部必须被赋值。在方法结束前，必须保证为out参数分配了一个值。
    //              2、在调用方法之前，无需为out参数赋值
    //              3、在调用方法时，使用out关键字声明参数
    public class OutParam
    {
        //声明方法  ，带有两个输出参数
        public static void Divide(int dividend, int divisor, out int quotient, out int remainder)
        {
            quotient = dividend / divisor;
            remainder = dividend % divisor;
        }
        //本方法中调用了上面带有输出参数的方法
        public static string test()
        {
            int dividend = 10;
            int divisor = 3;
            int result, remainder;
            Divide(dividend, divisor, out result, out remainder);
            return "result = " + result.ToString() + " , remainder= " + remainder.ToString();
        }
    }



    //4.3  in  参数（传递只读参数）
    //              在C#中，in参数是一种传递参数的方式，用于将参数按照只读的方式传递给方法，以提高性能，并避免不必要的拷贝。
    //             使用in参数时，参数的值不可以被修改，且方法不能对其分配。
    /**
            以下是使用in参数时的几个重点：
                1、传递只读参数： in参数用于传递只读的参数值给方法。这意味着方法内部无法修改传递的参数值，只能对其进行读取操作。
                2、避免拷贝： 是用in参数可以避免在传递参数时进行不必要的拷贝。通常情况下传递结构类型和值类型的的参数时，会发生参数拷贝。
                              使用in参数可以避免这种拷贝，提高性能。
                3、不可分配： 在方法内部，不能对in参数进行分配呢操作。这意味着不能将in参数赋值给其他变量，也不能修改参数内部的字段或属性。
    **/
    class TestIn
    {
        static string ReturnValue(in int x, in int y)//C# 7.0中不支持只读参数传递
        {
            return "x = " + x.ToString() + " , y= " + y.ToString(); 
        }

        private string test()
        {
            int a = 10;
            int b = 11;
            return ReturnValue(in a,in b);//以只读方式传递参数
        }
    }

    /**
     *      需要注意的是，in参数适用于传递大型结构体或值类型参数时，以避免不必要的拷贝操作。但在其他情况下，使用in参数可能对性能产生微弱或无明显的影响。
     **/
}
